﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mono.Cecil;
using devtm.Aop.Emit;
using Mono.Cecil.Cil;
using System.Reflection;

namespace devtm.Aop.Extensions
{
    
    public static class CilWorkerExt
    {

        public static bool IsVoid(this CilWorker gen) { return gen.Body.Method.ReturnType.Name == "Void"; }

        public static Instruction WriteInteger(this CilWorker gen, int value)
        {

            switch (value)
            {
                case 0:
                    return gen.Insert(OpCodes.Ldc_I4_0);
                case 1:
                    return gen.Insert(OpCodes.Ldc_I4_1);
                case 2:
                    return gen.Insert(OpCodes.Ldc_I4_2);
                case 3:
                    return gen.Insert(OpCodes.Ldc_I4_3);
                case 4:
                    return gen.Insert(OpCodes.Ldc_I4_4);
                case 5:
                    return gen.Insert(OpCodes.Ldc_I4_5);
                case 6:
                    return gen.Insert(OpCodes.Ldc_I4_6);
                case 7:
                    return gen.Insert(OpCodes.Ldc_I4_7);
                case 8:
                    return gen.Insert(OpCodes.Ldc_I4_8);
                default:
                    //return gen.Insert(OpCodes.Ldc_I4_M1, value);
                    return gen.Insert(OpCodes.Ldc_I4, value);
            }
        }


        public static void NewArray(this CilWorker gen, TypeReference type, int lenght)
        {
            WriteInteger(gen, lenght);
            gen.Insert(OpCodes.Newarr, type);
        }


        public static Instruction GetStockLocation(this CilWorker gen, VariableDefinition variable)
        {

            switch (variable.Index)
            {
                case 0:
                    return gen.Insert(OpCodes.Ldloc_0);
                case 1:
                    return gen.Insert(OpCodes.Ldloc_1);
                case 2:
                    return gen.Insert(OpCodes.Ldloc_2);
                case 3:
                    return gen.Insert(OpCodes.Ldloc_3);
                default:
                    return gen.Insert(OpCodes.Ldloc_S, variable);
            }
        }


        public static Instruction SetStockLocation(this CilWorker gen, VariableDefinition variable)
        {

            switch (variable.Index)
            {
                case 0:
                    return gen.Insert(OpCodes.Stloc_0);
                case 1:
                    return gen.Insert(OpCodes.Stloc_1);
                case 2:
                    return gen.Insert(OpCodes.Stloc_2);
                case 3:
                    return gen.Insert(OpCodes.Stloc_3);
                default:
                    return gen.Insert(OpCodes.Stloc_S, variable);
            }
        }


        public static Instruction WriteString(this CilWorker gen, string text)
        {
            return gen.Insert(OpCodes.Ldstr, text);
        }



        public static Instruction WriteParameterArgument(this CilWorker gen, ParameterDefinition parameter)
        {
            Instruction retour = null;
            
            switch (parameter.Index + 1)
            {
                //case 0:
                //    retour = gen.Insert(OpCodes.Ldarg_0);
                //    break;
                case 1:
                    retour = gen.Insert(OpCodes.Ldarg_1);
                    break;
                case 2:
                    retour= gen.Insert(OpCodes.Ldarg_2);
                    break;
                case 3:
                    retour= gen.Insert(OpCodes.Ldarg_3);
                    break;
                default:
                    //return gen.Insert(OpCodes.Ldc_I4_M1, lenght);
                    retour= gen.Insert(OpCodes.Ldarg_S, parameter);
                    break;
            }

            //Need boxing

            return retour;

        }


        public static void WriteCustomAttributeArgumentValue(this CilWorker gen, CustomAttributeArgument cArgument)
        {

            ModuleDefinition _module = gen.Body.Method.Module;

            TypeReference t = _module.Import(cArgument.Type.Resolve());

            if (t.Name == "String")
            {
                gen.Insert(OpCodes.Ldstr, cArgument.Value as string);
            }
            else if (t.Name == "Int32")
            {
                gen.WriteInteger((Int32)cArgument.Value);
                gen.Insert(OpCodes.Box, t);
            }
            else if (t.Name == "Int64")
            {
                gen.Insert(OpCodes.Ldc_I8, (long)cArgument.Value);
                gen.Insert(OpCodes.Box, t);
            }
            else if (t.Name == "Boolean")
            {

                throw new NotImplementedException();

                //gen.Insert(OpCodes.Ldc_I8, (bool)cArgument.Value);
                //gen.Insert(OpCodes.Box, t);
            }
            else if (t.Name == "Double")
            {

                throw new NotImplementedException();


                gen.Insert(OpCodes.Ldc_I8, (long)cArgument.Value);
                //gen.Insert(OpCodes.Box, t);
            }
            else if (t.Name == "Type")
            {

                MethodReference method2 = _module.Import(typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(RuntimeTypeHandle) }, null));

                gen.Insert(OpCodes.Ldtoken, _module.Import(((TypeReference)cArgument.Value)));
                gen.Insert(OpCodes.Call, method2);      // L_0036: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)

            }
            else
            {
                throw new NotImplementedException();
            }


        }



        
        public static void Box(this CilWorker gen, TypeReference t)
        {

            TypeDefinition type = t.IsDefinition ? t as TypeDefinition : t.Resolve();
            
            if (type.Name == "String")
            {
                //gen.Insert(OpCodes.Ldstr, cArgument.Value as string);
            }
            else if (type.Name == "Int32")
            {
                gen.Insert(OpCodes.Box, type);
            }
            else if (type.Name == "Int64")
            {
                gen.Insert(OpCodes.Box, type);
            }
            else if (type.Name == "Boolean")
            {

                throw new NotImplementedException();

                //gen.Insert(OpCodes.Ldc_I8, (bool)cArgument.Value);
                //gen.Insert(OpCodes.Box, t);
            }

            else if (type.Name == "Type")
            {

                throw new NotImplementedException();


                //MethodReference method2 = gen.Body.Method.Module.Import(typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(RuntimeTypeHandle) }, null));

                //gen.Insert(OpCodes.Ldtoken, gen.Body.Method.Module.Import(((TypeReference)cArgument.Value)));
                //gen.Insert(OpCodes.Call, method2);      // L_0036: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)

            }
            else
            {
            //    throw new NotImplementedException();
            }

        }
        
        


    }
}
